#include "Spark/Net/Impl/Acceptor.h"
#include "Spark/Utils/Logger.h"
#include "Spark/Net/EventLoop.h"
#include "Spark/Net/SocketAddr.h"
#include "Spark/Net/Socket.h"
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>

Acceptor::Acceptor(EventLoop* loop, const SocketAddr& addr)
    : loop_(loop),
      socket_(Socket::createSocket(addr.family())),
      addr_(addr),
      acceptorChannel_(loop_, socket_.fd()),
      idleFD_(::open("/dev/null", O_RDONLY | O_CLOEXEC))
{
    socket_.setSocketOption(SO_::REUSEADDR, 1);
    socket_.setSocketOption(SO_::REUSEPORT, 1);
#ifndef NDBUG
    socklen_t rp{}, ra{};
    socklen_t rplen{}, ralen{};
    ::getsockopt(socket_.fd(), SOL_SOCKET, SO_REUSEADDR, &ra, &ralen);
    LOG_SYSERR << "GETSOCKOPT REUSEADDR ret=" << ra;
    ::getsockopt(socket_.fd(), SOL_SOCKET, SO_REUSEPORT, &rp, &rplen);
    LOG_SYSERR << "GETSOCKOPT REUSEPORT ret=" << rp;
#endif
    socket_.bind(&addr_);
    acceptorChannel_.setCallback(CBType::READ, [this] { handleRead(); });
}

Acceptor::~Acceptor()
{
    acceptorChannel_.disableAll();
    acceptorChannel_.remove();
    ::close(idleFD_);
}

void Acceptor::listen()
{
    loop_->isInLoopThread();
    isListening_ = true;
    socket_.listen();
    acceptorChannel_.addReadEvent();
}

void Acceptor::handleRead()
{
    SocketAddr peer{};
    int clientfd = socket_.accept(&peer);
    if (clientfd >= 0)
    {
        if (newConnectionCallback_)  // if callback is set
            newConnectionCallback_(clientfd, peer);
        else  // otherwise close on connect
            ::close(clientfd);
    }
    else  // accept error
    {
        LOG_SYSERR << "Acceptor::handleRead()";
        if (errno == EMFILE)  // not enough file descriptor
        {
            ::close(idleFD_);
            idleFD_ = ::accept(socket_.fd(), nullptr, nullptr);
            ::close(idleFD_);  // reject connection
            idleFD_ = ::open("/dev/null", O_RDONLY | O_CLOEXEC);
        }
    }
}